package service

import (
	"Infinichat_mixed/back/mapper"
	"Infinichat_mixed/back/utils"
	"Infinichat_mixed/entity"
	"Infinichat_mixed/global_utils"
	"Infinichat_mixed/server/s_utils"
	"encoding/json"
	"fmt"
	"github.com/asaskevich/govalidator"
	"github.com/gin-gonic/gin"
	"math/rand"
	"strconv"
)

// GetUserList
// @Tags 用户模块
// @param page formData string false "页码"
// @param size formData string false "条数"
// @Success 200 {string} json{"code","message"}
// @Router /user/getUsers [post]
func GetUserList(c *gin.Context) {
	page, _ := strconv.Atoi(c.PostForm("page"))
	size, _ := strconv.Atoi(c.PostForm("size"))

	// 先计算是否超过50条（也就是redis的极限），如果超过50条，则不用查redis
	// [begin,end)
	begin := (page - 1) * size
	end := begin + size

	data := make([]*entity.User, size)

	// 即需要取的数据在[0,100)中
	flag_dataRedis := false
	//conn := utils.RedisPool.Get()
	//defer conn.Close()
	if end <= 50 {
		// 先确定redis中是否有缓存50条数据
		// 注意：redis不一定有50条数据的！如果第一次查的情况下。
		// 但是，引申出个思考：其实可以给被查的用户上个freq，然后redis中保留最高的freq
		//res, err := redis.StringMap(conn.Do("Hgetall", "users"))
		res, err := s_utils.RedisClient.HGetAll(s_utils.Ctx, "users").Result()
		if err != nil {
			return
		}
		if len(res) != 0 {
			// 在redis中查到了
			fmt.Println("data in Redis!")
			flag_dataRedis = true
			// 取((page-1)*size, page*size)的数据
			i := 0
			for k, v := range res {
				user_tmp := &entity.User{}
				err := json.Unmarshal([]byte(v), &user_tmp)
				if err != nil {
					fmt.Println("unmarshal err:", err)
					continue
				}
				//fmt.Println("user_tmp:", user_tmp)
				index, _ := strconv.Atoi(k)
				if index > ((page-1)*size) && index <= page*size {
					var user *entity.User
					err = json.Unmarshal([]byte(v), &user)
					if err != nil {
						fmt.Println("unmarshal err:", err)
						continue
					}
					data[i] = user
					i += 1
					if i == size {
						break
					}
				}
			}
			c.JSON(200, gin.H{
				"message": data,
			})
			return
		}
	}

	// 去mysql中查
	// 如果redis中没有数据 则先从mysql中取出前100条放在redis中
	if flag_dataRedis == false {
		dataInRedis := make([]*entity.User, 50) // 注意：这个50应该是从配置中读取更合适!
		utils.DB.Limit(50).Order("ID asc").Find(&dataInRedis)
		// 存入redis
		for _, v := range dataInRedis {
			res, err := json.Marshal(v)
			if err != nil {
				fmt.Println("res:", res, "raise err:", err)
				continue
			}
			// 这里注意要 string(res)
			//conn.Do("HSet", "users", v.ID, string(res))
			_ = s_utils.RedisClient.HSet(s_utils.Ctx, "users", v.ID, string(res)).Err()
		}

		if end <= 50 {
			// 不需要二次查询MYSQL
			ptr := 0
			for i := begin; i < end; i++ {
				data[ptr] = dataInRedis[i]
				ptr += 1
			}
			c.JSON(200, gin.H{
				"message": data,
			})
			return
		}
	}

	// 笔记：
	//data := make([]*User, 2)
	//s_utils.DB.Find(&data)	// 这样做，还是将全部数据查出
	//s_utils.DB.Limit(2).Find(&data) // 这样才是查出两条

	// 用户要求所查>50
	utils.DB.Offset(begin).Limit(size).Find(&data)
	c.JSON(200, gin.H{
		"message": data,
	})
	return
}

// UserLogin
// @Tags 用户模块
// @Summmary UserLogin
// @param account formData string false "账号"
// @param nickname formData string false "昵称"
// @param password formData string false "密码"
// @param repassword formData string false "确认密码"
// @Success 200 {string} json{"code","message"}
// @Router /user/CreateUser [get]
func UserLogin(c *gin.Context) {
	user := &entity.User{}
	user.Account = c.PostForm("account")
	plain_password := c.PostForm("password")
	fmt.Println(user)
	// 验证账号是否正确
	retUser := mapper.FindUserByAccount(user.Account)
	if retUser.ID == 0 {
		// 账号不存在
		c.JSON(400, gin.H{
			"message": "账号不存在，请注册！",
		})
		return
	}
	fmt.Println(retUser)
	// 拿到盐 进行解码
	salt := retUser.Salt
	pwd_inDB := global_utils.MakePassword(plain_password, salt)
	ap := mapper.FindUserByAP(user.Account, pwd_inDB)
	if ap.ID == 0 {
		// 密码错误
		c.JSON(400, gin.H{
			"message": "密码错误！",
		})
		return
	}
	c.JSON(200, gin.H{
		"message": "登录成功！",
		"data":    ap,
	})
	return
}

// userRegister
// @Tags 用户模块
// @Summmary userRegister
// @param account formData string false "账号"
// @param nickname formData string false "昵称"
// @param password formData string false "密码"
// @param repassword formData string false "确认密码"
// @Success 200 {string} json{"code","message"}
// @Router /user/CreateUser [get]
func UserRegister(c *gin.Context) {
	user := &entity.User{}
	user.Account = c.PostForm("account")
	user.Nickname = c.PostForm("nickname")
	password := c.PostForm("password")
	repassword := c.PostForm("repassword")
	if password != repassword {
		c.JSON(400, gin.H{
			"message": "两次密码不一致！",
		})
		return
	}

	// 防止重复注册
	retUser := mapper.FindUserByAccount(user.Account)
	if retUser.ID != 0 {
		c.JSON(400, gin.H{
			"message": "用户已注册！",
		})
		return
	}

	// 加密
	salt := fmt.Sprintf("%06d", rand.Int31())
	user.Salt = salt
	user.Password = global_utils.MakePassword(password, salt)
	utils.DB.Create(user)
	c.JSON(200, gin.H{
		"message": "新增用户成功！",
	})
	return
}

// UpdateUser
// @Tags 用户模块
// @Summmary update user
// @param id formData string false "用户标识"
// @param nickname formData string false "昵称"
// @param password formData string false "密码"
// @param phone formData string false "手机号"
// @param email formData string false "邮箱"
// @Success 200 {string} json{"code","message"}
// @Router /user/UpdateUser [post]
func UpdateUser(c *gin.Context) {
	// 注意上述注解中，应该是formData!!!
	user := &entity.User{}
	id, _ := strconv.Atoi(c.PostForm("id"))
	user.ID = uint(id)
	user.Nickname = c.PostForm("nickname")
	user.Password = c.PostForm("password")
	user.Phone = c.PostForm("phone")
	user.Email = c.PostForm("email")
	fmt.Println("update:", user)
	_, err := govalidator.ValidateStruct(user)
	if err != nil {
		c.JSON(400, gin.H{
			"message": "参数不匹配！",
		})
		return
	}
	utils.DB.Model(&entity.User{}).Update(user)
	c.JSON(200, gin.H{
		"message": "修改用户成功！",
	})
}

// ==================================== test Data ========================================================================
func Add100Users() {
	for i := 0; i < 100; i++ {
		user := &entity.User{}
		user.Account = "user" + strconv.Itoa(i)
		user.Phone = "15896324572"
		user.Email = "123456@qq.com"
		user.Salt = fmt.Sprintf("%06d", rand.Int31())
		user.Password = global_utils.MakePassword("123456", user.Salt)
		utils.DB.Create(user)
	}
}
